package com.senlingspace.payment.core;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.base.Stopwatch;
import com.senlingspace.common.constant.HttpStatus;
import com.senlingspace.common.core.domain.AjaxResult;
import com.senlingspace.common.core.redis.RedisLock;
import com.senlingspace.common.enums.LakalaPaymentTypeEnum;
import com.senlingspace.common.enums.OrderSource;
import com.senlingspace.common.exception.ServiceException;
import com.senlingspace.common.request.OrderRequest;
import com.senlingspace.common.request.PaymentRequest;
import com.senlingspace.common.request.PrepaymentRequest;
import com.senlingspace.common.request.RepaymentRequest;
import com.senlingspace.common.utils.bean.BeanUtils;
import com.senlingspace.framework.security.context.SecurityUtilExt;
import com.senlingspace.payment.douyin.bean.request.DouyinOrderRequest;
import com.senlingspace.payment.douyin.bean.request.DouyinShopQueryRequest;
import com.senlingspace.payment.douyin.bean.response.DouyinOrderResponse;
import com.senlingspace.payment.douyin.bean.response.DouyinShopQueryResponse;
import com.senlingspace.payment.douyin.service.DouYinShopService;
import com.senlingspace.system.domain.SlkjOrder;
import com.senlingspace.system.domain.SlkjShop;
import com.senlingspace.system.domain.vo.ReceiptInfoVo;
import com.senlingspace.system.service.ISlkjShopService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * 订单处理
 *
 * @author lebronjames
 * @date 2024/07/31
 */
@Slf4j
public abstract class AbstractOrderProcessTemplate {



    /**
     * 处理抖音创建订单（票类订单创建）
     *
     * @param orderRequest
     * @return {@link AjaxResult }
     */
    @Transactional
    public AjaxResult processOrderDouyin(OrderRequest orderRequest, String createBy, Long receiptAmount) {
        log.info("订单处理开始...");
        PaymentRequest request = new PaymentRequest();
        BeanUtils.copyProperties(orderRequest, request);
        // 手动处理 payInventories 列表
        if (orderRequest.getPayInventories() != null) {
            List<PaymentRequest.PayInventory> paymentInventories = new ArrayList<>();
            for (OrderRequest.PayInventory orderInventory : orderRequest.getPayInventories()) {
                PaymentRequest.PayInventory paymentInventory = new PaymentRequest.PayInventory();
                BeanUtils.copyProperties(orderInventory, paymentInventory);
                paymentInventories.add(paymentInventory);
            }
            request.setPayInventories(paymentInventories);
        }
        request.setPaymentMethod("CASH_PAY");
        Stopwatch stopwatch = Stopwatch.createStarted();
        JSONObject obj = new JSONObject();
        try {
//            log.info("1、校验请求参数");
//            this.validateRequest(request, OrderSource.DOUYIN);

            log.info("2、创建订单");
            SlkjOrder order = this.createOrder(request, createBy, OrderSource.DOUYIN);
            order.setPaymentType(LakalaPaymentTypeEnum.INCASH.getCode());
            order.setThirdOrderNumber(createBy);
            log.info("3、计算订单价格");
//            int totalAmount = this.calculateTotalPrice(request.getPayInventories(), OrderSource.DOUYIN);
            order.setTotalAmount(Math.toIntExact(receiptAmount));
            order.setDiscountAmount(0);
            order.setActualAmount(Math.toIntExact(receiptAmount));

            log.info("4、初始化订单状态");
            order = this.initOrderStatus(order, request.getPaymentMethod());

            log.info("5、保存订单");
            Long orderId = this.saveOrder(order, request.getPayInventories(), request.getPaymentMethod());
            obj.put("orderNumber", order.getOrderNumber());
            obj.put("orderId", orderId);

            log.info("订单处理共耗时：{}", stopwatch.stop());

            return AjaxResult.success("下单成功！", obj);
        } catch (ServiceException e) {
            log.error("订单处理异常：{}", e.getMessage());
            this.handleException(e, "订单处理异常", JSON.toJSONString(request));
            log.info("订单处理共耗时：{}", stopwatch.stop());
            if (e.getCode() != null && e.getCode() == HttpStatus.BE_PAYING) {
                return AjaxResult.warn(e.getMessage(), obj);
            }
            return AjaxResult.error(e.getMessage());
        } catch (RuntimeException e) {
            // 执行数据回滚...
            this.handleException(e, "订单处理异常，操作回滚", JSON.toJSONString(request));
            log.error("下单异常，触发回滚：{}", e.getMessage());
            log.info("订单处理共耗时：{}", stopwatch.stop());
            throw e;
        }
    }




    /**
     * 处理微信小程序创建订单（票类订单创建）
     *
     * @param orderRequest
     * @return {@link AjaxResult }
     */
    @Transactional
    public AjaxResult processOrderWeChat(OrderRequest orderRequest, String createBy, Long customerId) {
        log.info("订单处理开始...");
        PaymentRequest request = new PaymentRequest();
        BeanUtils.copyProperties(orderRequest, request);
        // 手动处理 payInventories 列表
        if (orderRequest.getPayInventories() != null) {
            List<PaymentRequest.PayInventory> paymentInventories = new ArrayList<>();
            for (OrderRequest.PayInventory orderInventory : orderRequest.getPayInventories()) {
                PaymentRequest.PayInventory paymentInventory = new PaymentRequest.PayInventory();
                BeanUtils.copyProperties(orderInventory, paymentInventory);
                paymentInventories.add(paymentInventory);
            }
            request.setPayInventories(paymentInventories);
        }
        request.setPaymentMethod("ONLINE_PAY");
        Stopwatch stopwatch = Stopwatch.createStarted();
        JSONObject obj = new JSONObject();
        try {
            log.info("1、校验请求参数");
            this.validateRequest(request, OrderSource.MINI_PROGRAM);

            log.info("2、创建订单");
            SlkjOrder order = this.createOrder(request, createBy, OrderSource.MINI_PROGRAM);
            order.setCustomerId(customerId);
            order.setPaymentType(LakalaPaymentTypeEnum.WECHAT.getCode());

            log.info("3、计算订单价格");
            int totalAmount = this.calculateTotalPrice(request.getPayInventories(), OrderSource.MINI_PROGRAM);
            order.setTotalAmount(totalAmount);
            order.setDiscountAmount(0);
            order.setActualAmount(totalAmount);

            log.info("4、初始化订单状态");
            order = this.initOrderStatus(order, request.getPaymentMethod());

            log.info("5、保存订单");
            Long orderId = this.saveOrder(order, request.getPayInventories(), request.getPaymentMethod());
            obj.put("orderNumber", order.getOrderNumber());
            obj.put("orderId", orderId);

            log.info("订单处理共耗时：{}", stopwatch.stop());

            return AjaxResult.success("下单成功！", obj);
        } catch (ServiceException e) {
            log.error("订单处理异常：{}", e.getMessage());
            this.handleException(e, "订单处理异常", JSON.toJSONString(request));
            log.info("订单处理共耗时：{}", stopwatch.stop());
            if (e.getCode() != null && e.getCode() == HttpStatus.BE_PAYING) {
                return AjaxResult.warn(e.getMessage(), obj);
            }
            return AjaxResult.error(e.getMessage());
        } catch (RuntimeException e) {
            // 执行数据回滚...
            this.handleException(e, "订单处理异常，操作回滚", JSON.toJSONString(request));
            log.error("下单异常，触发回滚：{}", e.getMessage());
            log.info("订单处理共耗时：{}", stopwatch.stop());
            throw e;
        }
    }


    /**
     * 处理门店订单
     *
     * @param request
     * @return {@link AjaxResult }
     */
    @Transactional
    public AjaxResult processOrder(PaymentRequest request, String createBy) {
        log.info("订单处理开始...");
        Stopwatch stopwatch = Stopwatch.createStarted();
        JSONObject obj = new JSONObject();
        try {
            log.info("1、校验请求参数");
            this.validateRequest(request, OrderSource.SHOP);

            log.info("2、创建订单");
            SlkjOrder order = this.createOrder(request, createBy, OrderSource.SHOP);

            log.info("3、计算订单价格");
            int totalAmount = this.calculateTotalPrice(request.getPayInventories(), OrderSource.SHOP);
            order.setTotalAmount(totalAmount);
            order.setDiscountAmount(0);
            order.setActualAmount(totalAmount);

            log.info("4、初始化订单状态");
            order = this.initOrderStatus(order, request.getPaymentMethod());

            log.info("5、保存订单");
            Long orderId = this.saveOrder(order, request.getPayInventories(), request.getPaymentMethod());
            obj.put("orderNumber", order.getOrderNumber());
            obj.put("orderId", orderId);

            log.info("6、处理支付");
            this.processPayment(order, request.getAuthCode(), request.getPaymentMethod());

            log.info("7、获取小票信息");
            ReceiptInfoVo receiptInfo = this.getReceiptInfo(order.getOrderId());

            log.info("订单处理共耗时：{}", stopwatch.stop());

            return AjaxResult.success("下单成功！", receiptInfo);
        } catch (ServiceException e) {
            log.error("订单处理异常：{}", e.getMessage());
            this.handleException(e, "订单处理异常", JSON.toJSONString(request));
            log.info("订单处理共耗时：{}", stopwatch.stop());
            if (e.getCode() != null && e.getCode() == HttpStatus.BE_PAYING) {
                return AjaxResult.warn(e.getMessage(), obj);
            }
            return AjaxResult.error(e.getMessage());
        } catch (RuntimeException e) {
            // 执行数据回滚...
            this.handleException(e, "订单处理异常，操作回滚", JSON.toJSONString(request));
            log.error("下单异常，触发回滚：{}", e.getMessage());
            log.info("订单处理共耗时：{}", stopwatch.stop());
            throw e;
        }
    }

    protected abstract void validateRequest(PaymentRequest request, OrderSource source);

    protected abstract SlkjOrder createOrder(PaymentRequest request, String createBy, OrderSource source);

    protected abstract int calculateTotalPrice(List<PaymentRequest.PayInventory> items, OrderSource source);

    protected abstract SlkjOrder initOrderStatus(SlkjOrder order, String paymentMethod);

    protected abstract Long saveOrder(SlkjOrder order, List<PaymentRequest.PayInventory> items, String paymentMethod);

    protected abstract void processPayment(SlkjOrder order, String authCode, String paymentMethod);

    /*=======================================================================*/
    /*=======================================================================*/

    /**
     * 门店 重新付款
     *
     * @param request
     * @return {@link AjaxResult }
     */
    @Transactional
    public AjaxResult processRepayment(RepaymentRequest request, String updateBy) {
        log.info("重新付款开始...");
        Stopwatch stopwatch = Stopwatch.createStarted();
        JSONObject obj = new JSONObject();
        try {
            log.info("1、校验请求参数");
            this.validateRequest(request.getOrderId(), request.getPaymentMethod());

            log.info("2、重新构建 payment_request_id");
            SlkjOrder order = this.resetPaymentRequestId(request.getOrderId(), updateBy);
            obj.put("orderNumber", order.getOrderNumber());
            obj.put("orderId", request.getOrderId());

            log.info("3、处理支付");
            this.processPayment(order, request.getAuthCode(), request.getPaymentMethod());

            log.info("4、获取小票信息");
            ReceiptInfoVo receiptInfo = this.getReceiptInfo(order.getOrderId());

            log.info("重新支付处理共耗时：{}", stopwatch.stop());

            return AjaxResult.success("下单成功！", receiptInfo);
        } catch (ServiceException e) {
            log.error("重新付款异常：{}", e.getMessage());
            this.handleException(e, "重新支付异常", JSON.toJSONString(request));
            log.info("订单处理共耗时：{}", stopwatch.stop());
            if (e.getCode() != null && e.getCode() == HttpStatus.BE_PAYING) {
                return AjaxResult.warn(e.getMessage(), obj);
            }
            return AjaxResult.error(e.getMessage());
        } catch (RuntimeException e) {
            this.handleException(e, "重新付款异常，触发回滚", JSON.toJSONString(request));
            log.error("重新付款异常，操作回滚：{}", e.getMessage());
            log.info("订单处理共耗时：{}", stopwatch.stop());
            throw e;
        }
    }

    protected abstract void validateRequest(long orderId, String paymentMethod);

    protected abstract SlkjOrder resetPaymentRequestId(long orderId, String updateBy);

    protected abstract void handleException(Exception e, String description, String params);

    protected abstract ReceiptInfoVo getReceiptInfo(long orderId);

    /**
     * 微信小程序预下单
     *
     * @param req
     * @return {@link AjaxResult }
     */
    @Transactional
    public AjaxResult processWxPrepayment(PrepaymentRequest req, String updateBy) {
        log.info("微信小程序预下单请求参数：{}", JSON.toJSONString(req));
        Stopwatch stopwatch = Stopwatch.createStarted();
        try {
            log.info("1、校验请求参数");
            SlkjOrder order = this.validateRequest(req);

            log.info("2、构建微信预支付信息");
            JSONObject result = this.processWxPayment(order, req.getPickupMethod(), req.getAddressId());
            log.info("预支付信息：{}", result);
            log.info("微信小程序预下单处理共耗时：{}", stopwatch.stop());

            return AjaxResult.success("下单成功！", result);
        } catch (ServiceException e) {
            log.error("微信小程序预下单异常：{}", e.getMessage());
            this.handleException(e, "重新支付异常", JSON.toJSONString(req));
            log.info("微信小程序预下单，共耗时：{}", stopwatch.stop());
            return AjaxResult.error(e.getMessage());
        } catch (RuntimeException e) {
            this.handleException(e, "微信小程序预下单异常，触发回滚，重置支付请求ID", JSON.toJSONString(req));
            log.error("微信小程序预下单异常，操作回滚，重置支付请求ID：{}", e.getMessage());
            this.resetPaymentRequestId(req.getOrderId(), updateBy);
            log.info("微信小程序预下单，共耗时：{}", stopwatch.stop());
            throw e;
        }
    }

    protected abstract SlkjOrder validateRequest(PrepaymentRequest request);

    protected abstract JSONObject processWxPayment(SlkjOrder order, String pickupMethod, String addressId);

}
